//
// Created by EmFi on 2023/5/17.
//

#ifndef DBMS_SERIALIZABLEBPLUSTREE_H
#define DBMS_SERIALIZABLEBPLUSTREE_H

#include <fstream>
#include "BPlusTree.h"

template <class K, class V>
class SerializableBPlusTree : public BPlusTree<K, V> {
private:
    string fn;
    fstream conn;
public:
    explicit SerializableBPlusTree(int order) : BPlusTree<K, V>(order) {}

    ~SerializableBPlusTree() override {
        if (this->conn.is_open()) {
            this->conn.close();
        }
    }

    bool open(string fn) {
        if (this->conn.is_open()) {
            return true;
        }
        this->fn = fn;
        this->conn.open(this->fn, ios::binary | ios::in | ios::out);
        if (!this->conn.is_open()) {
            this->conn.open(this->fn, ios::binary | ios::out);
            if (!this->conn.is_open()) {
                return false;
            }
            this->conn.close();
            this->conn.open(this->fn, ios::binary | ios::in | ios::out);
        }
        return true;
    }
    void read() {
        if (!this->conn.is_open()) {
            cout << "Error: Open a file using open() first." << endl;
            return;
        }

        // 将度指针移动到 0
        this->conn.seekg(0, ios::beg);

        int readOrder;
        this->conn.read((char*)&readOrder, sizeof(int));
        if (readOrder != this->order) {
            cout << "Error: Not read. Cause of file.order not equals to tree.order." << endl;
            return;
        }

        bool rootExist;
        this->conn.read((char*)&rootExist, sizeof(bool));
        if (!rootExist) {
            delete this->root;
            return;
        }

        bool rootType;
        this->conn.read((char*)&rootType, sizeof(bool));
        if (rootType) {
            this->root = new InternalNode<K>(this->order);
        } else {
            this->root = new LeafNode<K, V>(this->order);
        }

        // 记录叶子结点，为重建双向链表做准备
        list<LeafNode<K, V>*> leaves;
        queue<Node<K> *> queue;
        queue.push(this->root);

        while (!queue.empty()) {
            Node<K> *node = queue.front();
            queue.pop();

            int length;
            this->conn.read((char*)&length, sizeof(int));
            node->setLength(length);
            this->conn.read((char*)node->getKeys(), sizeof(*node->getKeys()) * node->getCapacity());
            if (node->getType() == NodeType::LEAF) {
                this->conn.read(reinterpret_cast<char*>(((LeafNode<K, V>*)node)->getValues()), sizeof(*(((LeafNode<K, V>*)node)->getValues())) * node->getCapacity());
            } else {
                for (int i = 0; i < node->getLength() + 1; i++) {
                    bool nodeType;
                    this->conn.read((char*)&nodeType, sizeof(bool));
                    if (nodeType) {
                        auto *next = new InternalNode<K>(this->order);
                        next->setFather(node);
                        ((InternalNode<K>*) node)->setPointer(i, next);
                        queue.push(next);
                    } else {
                        auto *next = new LeafNode<K, V>(this->order);
                        next->setFather(node);
                        ((InternalNode<K>*) node)->setPointer(i, next);
                        // 记录叶子结点，为重建双向链表做准备
                        leaves.push_back(next);
                        queue.push(next);
                    }
                }
            }
        }

        // 重建叶子结点双向链表关系
        LeafNode<K, V>* lastLeaf = *leaves.begin();
        for (auto leaf = ++leaves.begin(); leaf != leaves.end(); leaf++) {
            lastLeaf->setNextNode(*leaf);
            ((LeafNode<K, V> *) *leaf)->setPrevNode(lastLeaf);
            lastLeaf = *leaf;
        }
    }
    void save() {
        if (!this->conn.is_open()) {
            cout << "Error: Open a file using open() first." << endl;
            return;
        }
        // 将写指针移动到 0
        this->conn.seekp(0, ios::beg);

        this->conn.write((char*)&this->order, sizeof(int));

        bool rootExist = this->root != nullptr;
        this->conn.write((char*)&rootExist, sizeof(bool));

        if (!rootExist) {
            this->conn.flush();
            return;
        }

        bool rootType = (this->root->getType() == NodeType::INTERNAL);
        this->conn.write((char*)&rootType, sizeof(bool));

        queue<Node<K> *> queue;
        queue.push(this->root);

        while (!queue.empty()) {
            Node<K> *node = queue.front();
            queue.pop();

            int length = node->getLength();
            this->conn.write((char*)&length, sizeof(int));
            this->conn.write((char*)node->getKeys(), sizeof(*node->getKeys()) * node->getCapacity());
            if (node->getType() == NodeType::LEAF) {
                this->conn.write((char*) (((LeafNode<K, V>*)node)->getValues()), sizeof(*(((LeafNode<K, V>*)node)->getValues())) * node->getCapacity());
            } else {
                for (int i = 0; i < node->getLength() + 1; i++) {
                    bool nodeType = node->getPointer(i)->getType() == NodeType::INTERNAL;
                    this->conn.write((char*)&nodeType, sizeof(bool));
                    queue.push(((InternalNode<K>*)node)->getPointer(i));
                }
            }
        }

        this->conn.flush();
    }
    void clear() override {
        BPlusTree<K, V>::clear();
        this->save();
    }
};

#endif //DBMS_SERIALIZABLEBPLUSTREE_H
